package com.roncoo.education.oauth.service;

import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.util.StringUtils;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.auth0.jwt.interfaces.DecodedJWT;
import com.roncoo.education.common.core.tools.Constants;
import com.roncoo.education.common.core.tools.JWTUtil;
import com.roncoo.education.course.feign.interfaces.IFeignExperimentScore;
import com.roncoo.education.course.feign.interfaces.score.constant.GetAccesstokenCodeConst;
import com.roncoo.education.course.feign.interfaces.score.constant.RefreshTokenCodeConst;
import com.roncoo.education.course.feign.interfaces.vo.ExperimentViewVO;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;

@Api(tags = "OAUTH TOKEN")
@RestController
@RequestMapping("/oauth")
@Slf4j
public class AuthController {
    
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    @Autowired
    private TokenEndpoint tokenEndpoint;
    
    @Autowired
    private IFeignExperimentScore experimentScore;

    /**
     * 实验详情接口
     */
//    @ApiOperation(value = "OAUTH TOKEN 1", notes = "OAUTH TOKEN 2")
//    @RequestMapping(value = "token", method = {RequestMethod.GET, RequestMethod.POST})
//    public Object save() {
////        return Result.success(ThreadContext.userId());
//    	return "abc";
//    }

    /**
     * 返回值 {code} {msg}
     * 0	调用成功，返回所需数据 
     * 1	参数错误：ticket缺失/appid缺失/signature缺失
     * 2	密钥错误：signature不匹配。请注意签名时如有encode参数，均需要decode解码处理  
     * 3	ticket过期
     * 4	无效ticket 
     * 其他值	系统错误或非法操作
     */
    @ApiOperation(value = "获取 access_token", notes = "OAUTH TOKEN 2")
    @RequestMapping(value = "token/accesstoken", method = {RequestMethod.GET, RequestMethod.POST})    
    public Object getAccesstoken(@RequestParam(required = true) Long appid, @RequestParam(required = true) String ticket, @RequestParam(required = true) String signature) throws HttpRequestMethodNotSupportedException {    	
    	Map<String, Object> ret = new HashMap<String, Object>();
    	
        // 头部
        if (StringUtils.isEmpty(appid) || StringUtils.isEmpty(ticket) || StringUtils.isEmpty(signature)) {
			ret.put("code", GetAccesstokenCodeConst.code_1);
			ret.put("msg", GetAccesstokenCodeConst.code_1_msg);
			return ret;
        }

        ticket = URLDecoder.decode(ticket);
        
        if (!stringRedisTemplate.hasKey(ticket)) {
			ret.put("code", GetAccesstokenCodeConst.code_3);
			ret.put("msg", GetAccesstokenCodeConst.TicketCode_3_msg);
			return ret;
        }

        // 解析 token
        DecodedJWT jwt = null;
        try {
            jwt = JWTUtil.verify(ticket);
        } catch (Exception e) {
            log.error("ticket异常，ticket={}", ticket);
//            throw new BaseException(ResultEnum.TOKEN_ERROR);
			ret.put("code", GetAccesstokenCodeConst.code_4);
			ret.put("msg", GetAccesstokenCodeConst.code_4_msg);
			return ret;
        }

        // 校验token
        if (null == jwt) {
//            throw new BaseException(ResultEnum.TOKEN_ERROR);
			ret.put("code", GetAccesstokenCodeConst.code_4);
			ret.put("msg", GetAccesstokenCodeConst.code_4_msg);
			return ret;
        }
        Long userId = JWTUtil.getUserId(jwt);
        if (userId <= 0) {
//            throw new BaseException(ResultEnum.TOKEN_ERROR);
			ret.put("code", GetAccesstokenCodeConst.code_4);
			ret.put("msg", GetAccesstokenCodeConst.code_4_msg);
			return ret;
        }

        // 更新时间，使token不过期
        stringRedisTemplate.expire(ticket, Constants.SESSIONTIME, TimeUnit.MINUTES);
    	
    	
    	ExperimentViewVO experimenViewtVo = experimentScore.getExperimentById(appid);
    	if (experimenViewtVo == null) {
			ret.put("code", GetAccesstokenCodeConst.code_999);
			ret.put("msg", "appid 有误");
			return ret;
    	}

		// MD5(TICKET+ APPID+SECRET)
    	String appPassword = experimenViewtVo.getSecret();
		String preString = ticket + appid + appPassword;
		String signature2 = DigestUtils.md5Hex(preString);
		if (!signature2.toUpperCase().equals(signature.toUpperCase())) {    			
			ret.put("code", GetAccesstokenCodeConst.code_2);
			ret.put("msg", GetAccesstokenCodeConst.code_2_msg);
			ret.put("signature", signature2.toUpperCase());
			ret.put("signature_dis", "signature 为后台验证后的签名串，为了提高对接效率，后期会删掉");
			return ret;
		}
    	
//        return Result.success(ThreadContext.userId());
    	Map<String, String> parameters = new HashMap<String, String>();
    	parameters.put("username", userId.toString());
    	parameters.put("password", userId.toString());
    	parameters.put("grant_type", "password");

    	User user = new User(appid.toString(), "123456972", new ArrayList<>());
        
        Authentication principal = new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
        
        try {
            OAuth2AccessToken oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();
            return oAuth2AccessToken;
        } catch (Exception e) {
			ret.put("code", GetAccesstokenCodeConst.code_999);
			ret.put("msg", e);
			return ret;
        }

    }
    
    /**
     * 返回值 {code} {msg}
     * 0	调用成功，返回所需数据 
     * 1	参数错误：access_token缺失/appid缺失/signature缺失
     * 2	密钥错误：signature不匹配。请注意签名时如有encode参数，均需要decode解码处理
     * 3	无效access_token
     * 4	原始access_token不正确
     * 5	请重新获取access_token（重新获取次数超出2次） 
     * 其他值	非法请求或系统错误
     */    
    @ApiOperation(value = "刷新 access_token", notes = "OAUTH TOKEN 2")
    @RequestMapping(value = "token/refreshtoken", method = {RequestMethod.GET, RequestMethod.POST})        
    public Object refreshtoken(@RequestParam(required = true) Long appid, @RequestParam(required = true) String refresh_token, @RequestParam(required = true) String signature) throws HttpRequestMethodNotSupportedException {
    	Map<String, Object> ret = new HashMap<String, Object>();
        if (StringUtils.isEmpty(appid) || StringUtils.isEmpty(refresh_token) || StringUtils.isEmpty(signature)) {
			ret.put("code", RefreshTokenCodeConst.code_1);
			ret.put("msg", RefreshTokenCodeConst.code_1_msg);
			return ret;
        }
        
    	ExperimentViewVO experimenViewtVo = experimentScore.getExperimentById(appid);
    	if (experimenViewtVo == null) {
			ret.put("code", RefreshTokenCodeConst.code_999);
			ret.put("msg", "appid 有误");
			return ret;
    	}

		// MD5(TICKET+ APPID+SECRET)
    	String appPassword = experimenViewtVo.getSecret();
		String preString = refresh_token + appid + appPassword;
		String signature2 = DigestUtils.md5Hex(preString);
		if (!signature2.toUpperCase().equals(signature.toUpperCase())) {    			
			ret.put("code", RefreshTokenCodeConst.code_2);
			ret.put("msg", RefreshTokenCodeConst.code_2_msg);
			ret.put("signature", signature2.toUpperCase());
			ret.put("signature_dis", "signature 为后台验证后的签名串，为了提高对接效率，后期会删掉");
			return ret;
		}
    	
//        return Result.success(ThreadContext.userId());
    	Map<String, String> parameters = new HashMap<String, String>();
    	parameters.put("refresh_token", refresh_token);
    	parameters.put("grant_type", "refresh_token");

    	User user = new User(appid.toString(), "123456972", new ArrayList<>());
        
        Authentication principal = new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
        try {
            OAuth2AccessToken oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();
            return oAuth2AccessToken;
        } catch (Exception e) {
        	ret.put("code", RefreshTokenCodeConst.code_999);
			ret.put("msg", e);
			return ret;
        }

    }
    
    @GetMapping("token/md5")
    public Object md5(@RequestParam(required = true) String ticket, 
    		@RequestParam(required = true) Long appid, 
    		@RequestParam(required = true) String secret){
    	Map<String, Object> ret = new HashMap<String, Object>();
    	
		// MD5(TICKET+ APPID+SECRET)
		String preString = ticket + appid + secret;
		String signature2 = DigestUtils.md5Hex(preString);
		ret.put("code", 0);
		ret.put("signature", signature2.toUpperCase());

		return ret;
    } 
    
    /**
     * 自定义登录接口
     * @return
     */
    @ApiOperation(value = "OAUTH TOKEN 1", notes = "OAUTH TOKEN 2")
    @RequestMapping(value = "token2", method = {RequestMethod.GET, RequestMethod.POST})            
    public Object OAuth2AccessTokenlogin(@RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
        //注入clientId 和 password
        // 可以通过Header传入client 和 secret
    	
    	User user = new User("clientId", "123456", new ArrayList<>());
        
        Authentication principal = new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
        
        OAuth2AccessToken oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();
        return oAuth2AccessToken;
    }   
}  